package com.qingzhuge.web.interceptor;

import com.qingzhuge.autoconfigure.QingTingProperties;
import com.qingzhuge.common.HttpCode;
import com.qingzhuge.core.annotation.security.Anonymous;
import com.qingzhuge.core.annotation.security.Authorized;
import com.qingzhuge.exception.TokenException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author : zero.xiao
 * @description : 权限(Token)验证
 * @date :2018/6/8 0008 下午 14:23
 * @modified :
 */
@Slf4j
public abstract class BaseAuthorizationInterceptor extends HandlerInterceptorAdapter {


    @Autowired
    protected QingTingProperties qingTingProperties;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (handler instanceof HandlerMethod) {
            String uri = request.getRequestURI();
            return isAnonymous((HandlerMethod) handler, uri) || isAuthorized(request, response, (HandlerMethod) handler);
        } else {
            return true;
        }
    }

    /**
     * 匿名访问，不需要权限
     *
     * @param handler HandlerMethod
     * @return boolean
     */
    private boolean isAnonymous(HandlerMethod handler, String uri) {
        //查看方法上是否有匿名注解
        Anonymous anoymous = handler.getMethodAnnotation(Anonymous.class);
        if (null == anoymous) {
            //如果方法没有匿名注解，查看类上是否有匿名注解
            anoymous = handler.getBeanType().getAnnotation(Anonymous.class);
        }

        AtomicBoolean isAnonymous = new AtomicBoolean(null != anoymous);
        //如果没有匿名访问权限
        if (!isAnonymous.get()) {
            //查看配置中是否有排队权限验证的地址
            List<String> excludePath = qingTingProperties.getSecurity().getExcludeUrl();
            isPath(isAnonymous, uri, excludePath);
        }
        return isAnonymous.get();
    }

    /**
     * 需要权限控制
     *
     * @param request  HttpServletRequest
     * @param response HttpServletResponse
     * @param handler  Object
     * @return boolean
     */
    private boolean isAuthorized(HttpServletRequest request, HttpServletResponse response, HandlerMethod handler) {
        //查看方法上是否有权限注解
        Authorized authorized = handler.getMethodAnnotation(Authorized.class);
        if (null == authorized) {
            //如果方法没有权限注解，查看类上是否有权限注解
            authorized = handler.getBeanType().getAnnotation(Authorized.class);
        }
        AtomicBoolean isAuthorized = new AtomicBoolean(null != authorized);
        //如果没有匿名访问权限
        if (!isAuthorized.get()) {
            String uri = request.getRequestURI();
            //查看配置中是否有排队权限验证的地址
            List<String> authPath = qingTingProperties.getSecurity().getAuthUrl();
            isPath(isAuthorized, uri, authPath);
            if(isAuthorized.get() ){
                return this.getHeaderToken(request, response, handler);
            }
        }
        //如果权限注解为空，直接返回，否则验证token
        return null == authorized || this.getHeaderToken(request, response, handler);
    }

    /**
     * 配置url的权限需求
     * @param atomic 判定结果
     * @param url url
     * @param paths path
     */
    private void isPath(AtomicBoolean atomic, String url, List<String> paths) {
        for (String path : paths) {
            if (path.endsWith("**")) {
                path = path.replaceAll("\\*\\*", "");
                if (url.startsWith(path)) {
                    atomic.set(true);
                    break;
                }
            } else {
                if (url.equalsIgnoreCase(path)) {
                    atomic.set(true);
                    break;
                }
            }
        }
    }

    private boolean getHeaderToken(HttpServletRequest request, HttpServletResponse response, HandlerMethod handler) {
        String headerToken = qingTingProperties.getSecurity().getToken();
        //从header中获取token
        String token = request.getHeader(headerToken);
        //token为空
        if (StringUtils.isEmpty(token)) {
            throw new TokenException(HttpCode.TOKEN_IS_NULL.message());
        }
        log.debug("request token:{}", token);
        return getToken(token, request, response, handler);
    }

    /**
     * 获取权限令牌
     *
     * @param token    处理token
     * @param request  HttpServletRequest
     * @param response HttpServletResponse
     * @param handler  Object
     * @return boolean
     */
    protected abstract boolean getToken(String token, HttpServletRequest request, HttpServletResponse response, Object handler);
}
